/*
    Contact info:
    bhepple@freeshell.org
    http://bhepple.freeshell.org/scrmgr

    Copyright (C) 1991-2008 Bob Hepple

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; see the file COPYING.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

*/
/*
 * 
 *    Copyright (C) 1986 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Emacs.
 * 
 * GNU Emacs is distributed in the hope that it will be useful,
 * but without any warranty.  No author or distributor
 * accepts responsibility to anyone for the consequences of using it
 * or for whether it serves any particular purpose or works at all,
 * unless he says so in writing.
 * 
 * Everyone is granted permission to copy, modify and redistribute
 * GNU Emacs, but only under the conditions described in the
 * document "GNU Emacs copying permission notice".   An exact copy
 * of the document is supposed to have been given to you along with
 * GNU Emacs so that you can know how you may redistribute it all.
 * It should be in a file named COPYING.  Among other things, the
 * copyright notice and this notice must be preserved on all copies.
 * 
 *
 * For Emacs in SunView/Sun-Windows: (supported by Sun Unix v3.2)
 * Insert a notifier filter-function to convert all useful input 
 * to "key" sequences that emacs can understand.  See: Emacstool(1).
 *
 * Author: Jeff Peck, Sun Microsystems, Inc. <peck@sun.com>
 *
 * Original Idea: Ian Batten
 * Updated 15-Mar-88, Jeff Peck: set IN_EMACSTOOL, TERM, TERMCAP
 *
 * 27-Nov-90: Hacked by Bob Hepple (BH) to use in other curses applications
 *
 * Some differences to the normal emacstool:
 *
 * 1. The Copy and Paste keys are trapped here and use the Sunview 
 *    selection (instead of passing them to the application - emacs
 *    is hard-coded to use the older SunWindows mechanism).
 *
 * 2. Mouse button hits are only sent to the application on 'DOWN'. 'UP' 
 *    events are passed to the selection service. The reason for this is 
 *    that if the application gets the UP events and updates the screen,
 *    then the selectin service does not get a chance to set the mark.
 *    I freely confess that I do not understand this completely and beg
 *    you (gentle reader) to send me a better method.
 *
 * 3. As a consequence of 2., in order to set the selection mark, you need
 *    to press the left mouse button, move to another character position,
 *    move to the place you want to mark and _then_ release the button.
 *    The middle and right buttons are not passed to the application and
 *    therefore work per Sunview defaults. In fact, the only mouse event
 *    that the application sees is the left-button-down!! This is enough
 *    for what I need this thing for - namely to use SCRMGR under Sunview.
 *
 * 4. On systems with 'emacsclient' (BSD and System V with IPC), it is nice
 *    to set 'EDITOR=emacsclient; export EDITOR'
 *
 * 5. The frame is sized to 26x80.
 *
 * 6. This code only supports 'small' selection buffers - up to around 2K,
 *    or about 1 screens worth.
 */

/* BH - You will need this in /etc/termcap: 'untic' (/usr/5bin/infocmp can
	help with others ...)

cursestool|Application running under cursestool:\
	am:km:ms:co#80:li#34:mr=\E[7m:\
	bl=^G:md=\E[1m:cl=\f:cr=\r:le=\b:do=\n:\
	nd=\E[C:cm=\E[%i%p1%d;%p2%dH:up=\E[A:\
	DC=\E[%p1%dP:dc=\E[P:DL=\E[%p1%dM:dl=\E[M:\
	cd=\E[J:ce=\E[K:ta=\t:IC=\E[%p1%d@:ic=\E[247z:\
	AL=\E[%p1%dL:al=\E[L:sf=\n:kl=\E*jr:\
	kd=\E*nr:kr=\E*lr:ku=\E*hr:\
	kD=\E[249z:k1=\E*at:k2=\E*bt:k3=\E*ct:\
	k4=\E*dt:k5=\E*et:k6=\E*ft:k7=\E*gt:\
	k8=\E*ht:k9=\E*it:kh=\E*gr:kH=\E*mr:\
	kN=\E*or:kP=\E*ir:kI=\E[247z:\
	mr=\E[7m:se=\E[m:\
	ue=\E[m:r2=\E[s:\
	sa=\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;m:\
	me=\E[m:so=\E[7m:us=\E[4m:

... and, this compiled into terminfo with tic:

cursestool|Application running under cursestool,
	am, km, msgr,
	cols#80, lines#34, rev=\E[7m,
	bel=^G, bold=\E[1m, clear=\f, cr=\r, cub1=\b, cud1=\n,
	cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH, cuu1=\E[A,
	dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM, dl1=\E[M,
	ed=\E[J, el=\E[K, ht=\t, ich=\E[%p1%d@, ich1=\E[247z,
	il=\E[%p1%dL, il1=\E[L, ind=\n, kcub1=\E*jr,
	kcud1=\E*nr, kcuf1=\E*lr, kcuu1=\E*hr,
	kdch1=\E[249z, kf1=\E*at, kf2=\E*bt, kf3=\E*ct,
	kf4=\E*dt, kf5=\E*et, kf6=\E*ft, kf7=\E*gt,
	kf8=\E*ht, kf9=\E*it, khome=\E*gr, kll=\E*mr,
	knp=\E*or, kpp=\E*ir, kich1=\E[247z,
	rev=\E[7m, rmso=\E[m,
	rmul=\E[m, rs2=\E[s,
	sgr=\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;m,
	sgr0=\E[m, smso=\E[7m, smul=\E[4m,
 BH */

#include <suntool/sunview.h>
#include <suntool/tty.h>
#include <stdio.h>
#include <sys/file.h>
#include <suntool/seln.h> /* BH */

char *ctool_rcsid(void) {
    return("$Id: ctool.c,v 1.6 2008/02/19 13:15:50 bhepple Exp $");
}

#define BUFFER_SIZE 128               /* Size of all the buffers */

/* define WANT_CAPS_LOCK to make f-key T1 (aka F1) behave as CapsLock */
/* #define WANT_CAPS_LOCK */
#ifdef WANT_CAPS_LOCK
int caps_lock;		/* toggle indicater for f-key T1 caps lock */
static char *Caps = "[CAPS] ";		/* Caps Lock prefix string */
#define CAPS_LEN 7			/* strlen (Caps) */
#endif

static char *mouse_prefix = "\030\000";	/* C-X C-@ */
static int   m_prefix_length = 2;       /* mouse_prefix length */

static char *key_prefix = "\033*";  	/* ESC *   */
static int   k_prefix_length = 2;       /* key_prefix length */

static char *smcreate_name = "smcreate";	/* default run command */
static char buffer[BUFFER_SIZE];	/* send to ttysw_input */
static char *title = "";		/* initial title */

Frame frame;                            /* Base frame for system */
Tty ttysw;                              /* Where smcreate is */
int font_width, font_height;            /* For translating pixels to chars */

int console_fd = 0;		/* for debugging: setenv DEBUGCTOOL */
FILE *console;			/* for debugging: setenv DEBUGCTOOL */

Icon frame_icon;
/* make an icon_image for the default frame_icon */
static short default_image[258] = 
{
#include <images/terminal.icon>
};
mpr_static(icon_image, 64, 64, 1, default_image);


/*
 * Assign a value to a set of keys
 */
int
button_value (event)
     Event *event;
{
  int retval = 0;
  /*
   * Code up the current situation:
   *
   * 1 = MS_LEFT;
   * 2 = MS_MIDDLE;
   * 4 = MS_RIGHT;
   * 8 = SHIFT;
   * 16 = CONTROL;
   * 32 = META;
   * 64 = DOUBLE;
   * 128 = UP;
   */

  if (MS_LEFT   == (event_id (event))) retval = 1;
  if (MS_MIDDLE == (event_id (event))) retval = 2;
  if (MS_RIGHT  == (event_id (event))) retval = 4;

  if (event_shift_is_down (event)) retval += 8;
  if (event_ctrl_is_down  (event)) retval += 16;
  if (event_meta_is_down  (event)) retval += 32;
  if (event_is_up         (event)) retval += 128;
  return retval;
}

/*
 *  Variables to store the time of the previous mouse event that was
 *  sent to smcreate.
 *
 *  The theory is that to time double clicks while ignoreing UP buttons,
 *  we must keep track of the accumulated time.
 *
 *  If someone writes a SUN-SET-INPUT-MASK for CTOOL,
 *  That could be used to selectively disable UP events, 
 *  and then this cruft wouldn't be necessary.
 */
static long prev_event_sec = 0;
static long prev_event_usec = 0;

/*
 *  Give the time difference in milliseconds, where one second
 *  is considered infinite.
 */
int
time_delta (now_sec, now_usec, prev_sec, prev_usec)
     long now_sec, now_usec, prev_sec, prev_usec;
{
  long sec_delta = now_sec - prev_sec;
  long usec_delta = now_usec - prev_usec;
  
  if (usec_delta < 0) {		/* "borrow" a second */
    usec_delta += 1000000;
    --sec_delta;
  }
  
  if (sec_delta >= 10) 
    return (9999);		/* Infinity */
  else
    return ((sec_delta * 1000) + (usec_delta / 1000));
}

/*
 * Get input from the selection buffer and send it to the underlying
 * process as if it were typed ...
 */
static void get_selection()
{
    Seln_holder		holder;
    Seln_request	*buffer;
    char		*reply;

    holder = seln_inquire(SELN_SHELF);
    buffer = seln_ask(&holder, SELN_REQ_CONTENTS_ASCII, 0, 0);
    if (holder.state != SELN_NONE) {
	reply = buffer->data + sizeof(Seln_attribute);
	ttysw_input(ttysw, reply, strlen(reply));
    }
}


/*
 * Filter function to translate selected input events for smcreate
 * Mouse button events become ^X^@(button x-col y-line time-delta) .
 * Function keys: ESC-*{c}{lrt} l,r,t for Left, Right, Top; 
 * {c} encodes the keynumber as a character [a-o]
 */
static Notify_value
input_event_filter_function (window, event, arg, type)
     Window window;
     Event *event;
     Notify_arg arg;
     Notify_event_type type;
{
  struct timeval time_stamp;

  if (console_fd) fprintf(console, "Event: %d\n", event_id(event));

  /* UP L1 is the STOP key */
  if (event_id(event) == WIN_STOP) {
    ttysw_input(ttysw, "\007\007\007\007\007\007\007", 7);
    return NOTIFY_IGNORED;
  }

  /* UP L5, L6 & L7 is Expose, Copy & Open, let them pass to sunview */
  if (event_id(event) == KEY_LEFT(5) ||
      event_id(event) == KEY_LEFT(6) ||
      event_id(event) == KEY_LEFT(7))
    if(event_is_up (event)) 
      return notify_next_event_func (window, event, arg, type);
    else return NOTIFY_IGNORED;

  /* L8 is PASTE ... BH */
  if (event_id(event) == KEY_LEFT(8))
      if (event_is_up(event)) {
	  get_selection();
	  return NOTIFY_IGNORED;
      } else
	  return NOTIFY_IGNORED;

  if (event_is_button (event)) { 	      /* do Mouse Button events */
      if (event_is_up (event)) 
	  return notify_next_event_func (window, event, arg, type);
      
      time_stamp = event_time (event);
      ttysw_input (ttysw, mouse_prefix, m_prefix_length);
      sprintf (buffer, "(%d %d %d %d)\015", 
	       button_value (event),
	       event_x (event) / font_width,
	       event_y (event) / font_height,
	       time_delta (time_stamp.tv_sec, time_stamp.tv_usec,
			   prev_event_sec, prev_event_usec)
	       );
      ttysw_input (ttysw, buffer, strlen(buffer));
      prev_event_sec = time_stamp.tv_sec;
      prev_event_usec = time_stamp.tv_usec;
      return notify_next_event_func (window, event, arg, type);
      /*  return NOTIFY_IGNORED; */
  }
  
  { /* Do the function key events */
    int d;
    char c = (char) 0;
    if ((event_is_key_left  (event)) ?
	((d = event_id(event) - KEY_LEFT(1)   + 'a'), c='l') : 
	((event_is_key_right (event)) ?
	 ((d = event_id(event) - KEY_RIGHT(1) + 'a'), c='r') : 
	 ((event_is_key_top   (event)) ?
	  ((d = event_id(event) - KEY_TOP(1)  + 'a'), c='t') : 0)))
      {
	if (event_is_up(event)) return NOTIFY_IGNORED;
	if (event_shift_is_down (event)) c = c -  32;
	/* this will give a non-{lrt} for unshifted keys */
	if (event_ctrl_is_down  (event)) c = c -  64;
	if (event_meta_is_down  (event)) c = c + 128;
#ifdef WANT_CAPS_LOCK
/* set a toggle and relabel window so T1 can act like caps-lock */
	if (event_id(event) == KEY_TOP(1)) 
	  {
	    /* make a frame label with and without CAPS */
	    strcpy (buffer, Caps); 
	    title = &buffer[CAPS_LEN];
	    strncpy (title, (char *)window_get (frame, FRAME_LABEL),
		     BUFFER_SIZE - CAPS_LEN);
	    buffer[BUFFER_SIZE] = (char) 0;	
	    if (strncmp (title, Caps, CAPS_LEN) == 0)
	      title += CAPS_LEN; 		 /* already Caps */
	    caps_lock =  (caps_lock ? 0 : CAPS_LEN);
	    window_set(frame, FRAME_LABEL, (title -= caps_lock), 0);
	    return NOTIFY_IGNORED;
	  }
#endif
	ttysw_input (ttysw, key_prefix, k_prefix_length);
	sprintf (buffer, "%c%c", d, c);
	ttysw_input(ttysw, buffer, strlen(buffer));

	return NOTIFY_IGNORED;
      }
  }
  if ((event_is_ascii(event) || event_is_meta(event)) 
      && event_is_up(event)) return NOTIFY_IGNORED;
#ifdef WANT_CAPS_LOCK
/* shift alpha chars to upper case if toggle is set */
  if ((caps_lock) && event_is_ascii(event)
      && (event_id(event) >= 'a') && (event_id(event) <= 'z'))
    event_set_id(event, (event_id(event) - 32));
/* crufty, but it works for now. is there an UPCASE(event)? */
#endif
  return notify_next_event_func (window, event, arg, type);
}

main (argc, argv)
     int argc;
     char **argv;
{
  int error_code;	/* Error codes */
  
  if(getenv("DEBUGCTOOL"))
    console = fdopen (console_fd = open("/dev/console",O_WRONLY), "w");

  			/* do this first, so arglist can override it */
  frame_icon = icon_create (ICON_LABEL, "  ctool",
			    ICON_IMAGE, &icon_image,
			    0);

  putenv("IN_CTOOL=t");	/* notify subprocess that it is in CTOOL */

  if (putenv("TERM=cursestool") != 0)	/* TTYSW will be a TERM=cursestool window */
    {fprintf (stderr, "%s: Could not set TERM=cursestool, using `%s'\n",
	     argv[0], (char *)getenv("TERM")) ;};
  /*
   * If TERMCAP starts with a slash, it is the pathname of the
   * termcap file, not an entry extracted from it, so KEEP it!
   * Otherwise, it may not relate to the new TERM, so Nuke-It.
   * If there is no TERMCAP environment variable, don't make one.
   */
  {
    char *termcap ;	/* Current TERMCAP value */
    termcap = (char *)getenv("TERMCAP") ;
    if (termcap && (*termcap != '/'))
      {
	if (putenv("TERMCAP=") != 0)
	  {fprintf (stderr, "%s: Could not clear TERMCAP\n", argv[0]) ;} ;
      } ;
  } ;
  
  /* find command to run as subprocess in window */
  if (!(argv[0] = (char *)getenv("CTOOL")))	/* Set smcreate command name */
      argv[0] = smcreate_name;			
  for (argc = 1; argv[argc]; argc++)		/* Use last one on line */
    if(!(strcmp ("-rc", argv[argc])))		/* Override if -rc given */
      {
	int i = argc;
	argv[argc--]=0;		/* kill the -rc argument */
	if (argv[i+1]) {	/* move to agrv[0] and squeeze the rest */
	  argv[0]=argv[i+1];
	  for (; argv[i+2]; (argv[i]=argv[i+2],argv[++i]=0));
	}
      }

  strcpy (buffer, title);
  strncat (buffer, argv[0],		 /* append run command name */
	   (BUFFER_SIZE - (strlen (buffer)) - (strlen (argv[0]))) - 1);

  			/* Build a frame to run in */
  frame = window_create ((Window)NULL, FRAME,
			 WIN_ROWS, 26,
			 WIN_COLUMNS, 80,
			 FRAME_LABEL, buffer,
			 FRAME_ICON, frame_icon,
			 FRAME_ARGC_PTR_ARGV, &argc, argv,
			 0);

  /* Create a tty with smcreate in it */
  ttysw = window_create (frame, TTY, 
			 TTY_QUIT_ON_CHILD_DEATH, TRUE, 
			 TTY_BOLDSTYLE, 8, 
			 TTY_ARGV, argv, 
			 0);

  window_set(ttysw,
	     WIN_CONSUME_PICK_EVENTS, 
	     WIN_STOP,
	     WIN_MOUSE_BUTTONS, WIN_UP_EVENTS,
	     /* LOC_WINENTER, LOC_WINEXIT, LOC_MOVE, */
	     0,

	     WIN_CONSUME_KBD_EVENTS, 
	     WIN_STOP,
	     WIN_ASCII_EVENTS, 
	     WIN_LEFT_KEYS, WIN_TOP_KEYS, WIN_RIGHT_KEYS,
	     /* WIN_UP_ASCII_EVENTS, */
	     0,
	     0);

  font_height = (int)window_get (ttysw, WIN_ROW_HEIGHT);
  font_width  = (int)window_get (ttysw, WIN_COLUMN_WIDTH);

                                         /* Interpose my event function */
  error_code = (int)  notify_interpose_event_func 
    (ttysw, input_event_filter_function, NOTIFY_SAFE);

  if (error_code != 0)                       /* Barf */
    {
      fprintf (stderr, "notify_interpose_event_func got %d.\n", error_code);
      exit (1);
    }

  window_main_loop (frame);                  /* And away we go */
}

/* For emacs:
 * Local Variables:
 * tab-width: 8
 * End:
 */
